/** @file    MethodsCommon.cpp
 *  @author  Alessandro Polo
 *  @version $Id: MethodsCommon.cpp 2832 2010-08-02 22:53:46Z alex $
 *  @brief
 * The header for this class can be found in MethodsCommon.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <core/MethodsCommon.h>
 #include <core/List.h>
 #include <core/Table.h>
 #include <core/LogContext.h>
 #include <core/SecurityManager.h>

 // MethodList includes:
 #include <core/DataModels.h>
 // MethodRequirementsList includes:
 #include <core/BundleRequirements.h>
 // MethodInterfacesList includes:
 #include <core/InterfacesProvider.h>
 // MethodRetrieveObject includes:
 #include <core/Mutex.h>
 // MethodPropertyT (and implementations) includes:
 #include <core/PropertiesProvider.h>
 // MethodChMod and MethodChOwn includes:
 #include <core/Object.h>
 #include <core/PropertiesProvider.h>
 #include <core/MethodsProvider.h>
 #include <core/UserManager.h>
 // MethodDump includes:
 #include <core/PersistenceManager.h>

#define mLOG( LEVEL, MESSAGE ) { if ( this->log != NULL ) { this->log->operator()(LEVEL, MESSAGE); } };
#define mLOGv( LEVEL, FORMAT, ... ) { if ( this->log != NULL ) { this->log->operator()(LEVEL, FORMAT, __VA_ARGS__); } };


using namespace std;
namespace wosh {

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

MethodMessage::MethodMessage( const std::string& Name )
	: MethodT<const Message*, MethodRunner, Message*>(Name) { }

MethodMessage::MethodMessage( const std::string& Name, const std::string& Description, const Permission& Perm )
	: MethodT<const Message*, MethodRunner, Message*>(Name, Description, Perm) { }

Message* MethodMessage::exec( const Message* requestMessage ) {
	if ( requestMessage == NULL ) return NULL;
	Message* result = execT( requestMessage );
	return result;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

MethodRequest::MethodRequest( const std::string& Name )
	: MethodT<const Request*, MethodRunner, Response*>(Name) { }

MethodRequest::MethodRequest( const std::string& Name, const std::string& Description, const Permission& Perm )
	: MethodT<const Request*, MethodRunner, Response*>(Name, Description, Perm) { }

Message* MethodRequest::exec( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	Response* result = execT( request );
	Message* reply = requestMessage->replyTo( result );
	return reply;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

MethodMessageResponse::MethodMessageResponse( const std::string& Name )
	: MethodT<const Message*, MethodRunner, Response*>(Name) { }

MethodMessageResponse::MethodMessageResponse( const std::string& Name, const std::string& Description, const Permission& Perm )
	: MethodT<const Message*, MethodRunner, Response*>(Name, Description, Perm) { }

void MethodMessageResponse::setMethod( MethodRunner* pare, MethodMessageResponsePtr fptr ) {
	this->parent = pare;
	this->smMethodTPointer = fptr;
	PROFILING_INIT(this, this->parent);
 }

Message* MethodMessageResponse::exec( const Message* message ) {
	if ( message == NULL ) return NULL;
	Response* result = execT( message );
	Message* reply = message->replyTo( result );
	return reply;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

MethodWRESULT::MethodWRESULT( const std::string& Name )
	: Method(Name), parent(NULL), smMethodTPointer(NULL) { }

MethodWRESULT::MethodWRESULT( const std::string& Name, const std::string& Description, const Permission& Perm )
	: Method(Name, Description, Perm), parent(NULL), smMethodTPointer(NULL) { }

Message* MethodWRESULT::exec( const Message* requestMessage ) {
	if ( this->parent == NULL || this->smMethodTPointer == NULL ) return NULL;
	PROFILING_ENTER(this, 0)
	WRESULT ret = (this->parent->*smMethodTPointer)();
	PROFILING_EXIT(this, 0)
	Message* reply = NULL;
	if ( requestMessage != NULL )	reply = requestMessage->replyTo();
	else							reply = new Message();
	reply->setContent( new Response(this->name, NULL, ret) );
	return reply;
 }

void MethodWRESULT::setMethod( MethodRunner* pare, MethodWRESULTPtr fptr ) {
	this->parent = pare;
	this->smMethodTPointer = fptr;
	PROFILING_INIT(this, this->parent);
 }

void MethodWRESULT::clear() {
	Method::clear();
	this->parent = NULL;
	this->smMethodTPointer = NULL;
 }

short MethodWRESULT::compare(const Method& other) const {
	const MethodWRESULT& mt = dynamic_cast<const MethodWRESULT&>(other);
	if ( this->parent != mt.parent ) return 1;
	if ( this->smMethodTPointer != mt.smMethodTPointer ) return -1;
	return 0;
 }


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

MethodVoid::MethodVoid( const std::string& Name )
	: Method(Name), parent(NULL), smMethodTPointer(NULL) { }

MethodVoid::MethodVoid( const std::string& Name, const std::string& Description, const Permission& Perm )
	: Method(Name, Description, Perm), parent(NULL), smMethodTPointer(NULL) { }

Message* MethodVoid::exec( const Message* requestMessage ) {
	if ( this->parent == NULL || this->smMethodTPointer == NULL ) return NULL;
	PROFILING_ENTER(this, 0)
	(this->parent->*smMethodTPointer)();
	PROFILING_EXIT(this, 0)
	Message* reply = NULL;
	if ( requestMessage != NULL )	reply = requestMessage->replyTo();
	else							reply = new Message();
	reply->setContent( new Response(this->name, NULL, WRET_OK) );
	return reply;
 }

void MethodVoid::setMethod( MethodRunner* pare, MethodVoidPtr fptr ) {
	this->parent = pare;
	this->smMethodTPointer = fptr;
	PROFILING_INIT(this, this->parent);
 }

void MethodVoid::clear() {
	Method::clear();
	this->parent = NULL;
	this->smMethodTPointer = NULL;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

MethodWRESULTwSecurityToken::MethodWRESULTwSecurityToken( const std::string& Name )
	: MethodT<const SecurityToken*, MethodRunner, WRESULT>(Name) { }

MethodWRESULTwSecurityToken::MethodWRESULTwSecurityToken( const std::string& Name, const std::string& Description, const Permission& Perm )
	: MethodT<const SecurityToken*, MethodRunner, WRESULT>(Name, Description, Perm) { }

Message* MethodWRESULTwSecurityToken::exec( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	WRESULT ret = execT( requestMessage->getSecurityToken() );
	Message* reply = requestMessage->replyTo();
	reply->setContent( new Response(this->name, NULL, ret) );
	return reply;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

MethodList::MethodList( const std::string& Name )
	: Method(Name), extractor(NULL) { }

MethodList::MethodList( const std::string& Name, const std::string& Description, const Permission& Perm )
	: Method(Name, Description, Perm), extractor(NULL) { }

MethodList::~MethodList() {
	setDataFieldExtractor(NULL);
 }

void MethodList::setDataFieldExtractor( IDataFieldExtractor* data_extractor ) {
	if ( this->extractor != NULL ) delete this->extractor;
	this->extractor = data_extractor;
 }

Message* MethodList::exec( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	if ( request == NULL ) return NULL;
	if ( this->extractor == NULL ) return NULL;
	if ( this->extractor->getDataFieldCollector() == NULL ) return NULL;

	TableRenderer renderer(this->extractor);
	//todo renderer.setSecurityToken( requestMessage->getSecurityToken() );
	renderer.setHeaderMode(TableRenderer::HEADER_HORIZONTAL);		// should be a default setting
	renderer.setOrientation(TableRenderer::ORIENTATION_VERTICAL);	// should be a default setting

	Table* args = NULL;
	if ( !request->hasData() ) {
		// all default fields:
		this->extractor->getDataFieldCollector()->getDefaults( renderer.getFields() );
	 }
	else if ( request->getData()->isKindOf<Variant>() ) {
		const Variant* arg = request->getData()->as<Variant>();
		if ( arg != NULL ) {
			args = new Table(1, 2);
			args->set(arg->clone(), 0, 0);
			args->set<Variant>(true, 0, 1);
		 }
	 }
	else if ( request->getData()->isKindOf<List>() ) {
		const List* argslist = request->getData()->as<List>();
		if ( argslist != NULL ) {
			args = new Table(argslist->size(), 2);
			args->setColumn(argslist, 0);
		 }
	 }
	else if ( request->getData()->isKindOf<Table>() ) {
		const Table* argstbl = request->getData()->as<Table>();
		if ( argstbl != NULL ) {
			args = argstbl->clone();
		 }
	 }
	// parse argument => filtering, sorting, rendering
	if ( args != NULL ) {
		int iRow = -1;
		if ( (iRow = args->findRowOfItem(0,"index-start")) >= 0 )
			renderer.setPageStart( args->getAsVariant(iRow, 1).toInteger(-1) );
		if ( (iRow = args->findRowOfItem(0,"index-end")) >= 0 )
			renderer.setPageEnd( args->getAsVariant(iRow, 1).toInteger(-1) );
		if ( (iRow = args->findRowOfItem(0,"header-only")) >= 0 )
			renderer.setPage(0,0);
		if ( (iRow = args->findRowOfItem(0,"no-header")) >= 0 )
			renderer.setHeaderMode(TableRenderer::HEADER_NONE);
		if ( (iRow = args->findRowOfItem(0,"fields")) >= 0 ) {
			if ( args->getAsVariant(iRow, 1) == "defaults" )
				this->extractor->getDataFieldCollector()->getDefaults( renderer.getFields() );
			else if ( args->getAsVariant(iRow, 1) == "all" )
				this->extractor->getDataFieldCollector()->getAll( renderer.getFields() );
		 }
		delete args;
	 }
	if ( renderer.getFields().size() == 0 )
		this->extractor->getDataFieldCollector()->getDefaults( renderer.getFields() );

	Table* oTbl = renderer.create();
	Response* result = new Response( this->name, oTbl );
	Message* reply = requestMessage->replyTo( result );
	return reply;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

MethodRetrieve::MethodRetrieve( const std::string& Name )
	: Method(Name), extractor(NULL) { }

MethodRetrieve::MethodRetrieve( const std::string& Name, const std::string& Description, const Permission& Perm )
	: Method(Name, Description, Perm), extractor(NULL) { }

MethodRetrieve::~MethodRetrieve() {
	setItemExtractor(NULL);
 }

void MethodRetrieve::setItemExtractor( IItemExtractor* obj_extractor ) {
	if ( this->extractor != NULL ) delete this->extractor;
	this->extractor = obj_extractor;
 }

Message* MethodRetrieve::exec( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	if ( request == NULL ) return NULL;
	if ( this->extractor == NULL ) return NULL;

	ListRenderer renderer(this->extractor);
	//todo renderer.setSecurityToken( requestMessage->getSecurityToken() );

	Table* args = NULL;
	if ( !request->hasData() ) {
		// all items!?
	 }
	else if ( request->getData()->isKindOf<Variant>() ) {
		const Variant* arg = request->getData()->as<Variant>();
		if ( arg != NULL ) {
			renderer.setPrimaryKeyValue( arg );
		 }
	 }
	else if ( request->getData()->isKindOf<List>() ) {
		const List* argslist = request->getData()->as<List>();
		if ( argslist != NULL ) {
			args = new Table(argslist->size(), 2);
			args->setColumn(argslist, 0);
		 }
	 }
	else if ( request->getData()->isKindOf<Table>() ) {
		const Table* argstbl = request->getData()->as<Table>();
		if ( argstbl != NULL ) {
			args = argstbl->clone();
		 }
	 }
	// parse argument => filtering, sorting, rendering
	if ( args != NULL ) {
		int iRow = -1;
		if ( (iRow = args->findRowOfItem(0,"primary-key")) >= 0 )
			renderer.setPrimaryKeyValue( args->get(iRow, 1) );
		if ( (iRow = args->findRowOfItem(0,"index-start")) >= 0 )
			renderer.setPageStart( args->getAsVariant(iRow, 1).toInteger(-1) );
		if ( (iRow = args->findRowOfItem(0,"index-end")) >= 0 )
			renderer.setPageEnd( args->getAsVariant(iRow, 1).toInteger(-1) );
		delete args;
	 }

	List* oList = renderer.create();
	Response* result = new Response( this->name, oList );
	Message* reply = requestMessage->replyTo( result );
	return reply;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

MethodPropertyT::MethodPropertyT( const std::string& Name )
	: Method(Name), properties(NULL), log(NULL) { }

MethodPropertyT::MethodPropertyT( const std::string& Name, const std::string& Description, const Permission& Perm )
	: Method(Name, Description, Perm), properties(NULL), log(NULL) { }

void MethodPropertyT::setPropertiesProvider( PropertiesProvider* ptr ) { this->properties = ptr; }
void MethodPropertyT::setLogContext( LogContext* ptr ) { this->log = ptr; }

Message* MethodPropertyT::exec( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	Response* response = execRequest(requestMessage);
	if ( response == NULL ) return NULL;
	return requestMessage->replyTo( response );
 }

WRESULT MethodPropertyT::createMethods( MethodsProvider* provider, PropertiesProvider* props, LogContext* logcontext ) {
	if ( provider == NULL ) return WRET_ERR_PARAM;
	if ( props == NULL ) return WRET_ERR_PARAM;

	MethodPropertyGet* mmPropertyGet = provider->create<MethodPropertyGet>();
	mmPropertyGet->setPropertiesProvider( props );
	mmPropertyGet->setLogContext( logcontext );

	MethodPropertiesGet* mmPropertiesGet = provider->create<MethodPropertiesGet>();
	mmPropertiesGet->setPropertiesProvider( props );
	mmPropertiesGet->setLogContext( logcontext );

	MethodPropertySet* mmPropertySet = provider->create<MethodPropertySet>();
	mmPropertySet->setPropertiesProvider( props );
	mmPropertySet->setLogContext( logcontext );

	MethodPropertiesSet* mmPropertiesSet = provider->create<MethodPropertiesSet>();
	mmPropertiesSet->setPropertiesProvider( props );
	mmPropertiesSet->setLogContext( logcontext );

	MethodPropertyDel* mmPropertyDel = provider->create<MethodPropertyDel>();
	mmPropertyDel->setPropertiesProvider( props );
	mmPropertyDel->setLogContext( logcontext );

	MethodList* mmListProperties = provider->create<MethodList>( _METHOD_ListProperties, "List Properties", Permission(Permission::RX) );
	mmListProperties->setDataFieldExtractor( DataFieldExtractor<PropertyHolder>::createFor< DataAdapterMapT<std::string,PropertyHolder> >(*props, "Properties") );
	MethodRetrieveObject<PropertiesProvider>* mmGetProperties = provider->create< MethodRetrieveObject<PropertiesProvider> >( _METHOD_GetPropertiesProvider, "Get PropertiesProvider", Permission(Permission::RX) );
	mmGetProperties->configure(props, NULL, NULL);

	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* MethodPropertyDel::execRequest( const Message* requestMessage ) {
	const Request* request = requestMessage->getContent()->asRequest();
	if ( request == NULL ) return NULL;
	if ( !request->hasData() ) return request->replyResponse(WRET_ERR_PARAM, "Empty Data");
	if ( this->properties == NULL ) {
		mLOG(LOG_WARNING, ":MethodPropertyDel() PropertiesProvider reference is NULL" );
		return NULL;
	 }
	MutexLockerWrite mL(this->properties->getMutex());
	// lock any concurrent operation
	bool allowListWrite = SecurityManager::evalAuthorization( requestMessage->getSecurityToken(), &this->properties->getListPermission(), Permission::Write );
	if ( !allowListWrite ) // also validated missing security token
		return NULL; // silent
	string property_key;
	if ( request->getData()->isKindOf<Property>() ) {
		const Property* property_source = request->getData()->as<Property>();
		if ( property_source != NULL )
			property_key = property_source->getKey();
	 }
	else if ( request->getData()->isKindOf<Variant>() ) {
		const Variant* vkey = request->getData()->as<Variant>();
		if ( vkey != NULL )
			property_key = vkey->toString();
	 }
	mLOGv(LOG_DEBUG, ":MethodPropertyDel(%s)", property_key.c_str() );

	const PropertyHolder* propHolder = this->properties->find( property_key );
	if ( propHolder == NULL )
		return request->replyResponse(WRET_ERR_PARAM, "Property not found");
	propHolder->transactionBeginWrite();
	if ( propHolder->data == NULL ) {
		propHolder->transactionEnd();
		return request->replyResponse(WRET_ERR_INTERNAL, "Internal error (misaligned)");
	 }
	// eval permission, overwrite value (we won't update permission)
	bool allowWrite = SecurityManager::evalAuthorization( requestMessage->getSecurityToken(), &propHolder->data->getPermission(), Permission::Write );
	if ( !allowWrite ) {
		propHolder->transactionEnd();
		return request->replyResponse(WRET_ERR_SECURITY, "Not authorized to write property");
	 }
	delete propHolder->data; propHolder->data = NULL;
	propHolder->transactionEnd();
	WRESULT ret = this->properties->erase(property_key, true);
	return request->replyResponse(ret, new Variant(property_key));
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* MethodPropertySet::execRequest( const Message* requestMessage ) {
	const Request* request = requestMessage->getContent()->asRequest();
	if ( request == NULL ) return NULL;
	if ( !request->hasData() ) return request->replyResponse(WRET_ERR_PARAM, "Empty Data");
	if ( this->properties == NULL ) {
		mLOG(LOG_WARNING, ":MethodPropertySet() PropertiesProvider reference is NULL" );
		return NULL;
	 }
	MutexLockerRead mL(this->properties->getMutex());
	// lock invalidating operations (REMOVAL; List::Permission updates)
	bool allowread = SecurityManager::evalAuthorization( requestMessage->getSecurityToken(), &this->properties->getListPermission(), Permission::Read );
	if ( !allowread ) // also validated missing security token
		return NULL; // silent

	if ( request->getData()->isKindOf<Property>() ) {
		const Property* property_source = request->getData()->as<Property>();
		mLOGv(LOG_DEBUG, ":MethodPropertySet(%s) [Property]", property_source->getKey().c_str() );
		PropertyHolder* propHolder = this->properties->find( property_source->getKey() );
		if ( propHolder == NULL ) {
			// validate insertion against list permission, user must be granted to write the list
			bool allowAdd = SecurityManager::evalAuthorization( requestMessage->getSecurityToken(), &this->properties->getListPermission(), Permission::Write );
			if ( !allowAdd )
				return request->replyResponse(WRET_ERR_SECURITY, "Not allowed to add new Property");
			// user may add the property, 
			///@todo should validate property permission..
			// add/update (non-locking, callbacks)
			WRESULT ret = this->properties->set( property_source->getKey(), property_source->getValue(), property_source->getPermission(), Permission::Overwrite_All);
			// expensive, but useful to spread the updated property,
			// note that the whole operation is NOT atomic, following returned item MAY be != *property_source
			return request->replyResponse(ret, this->properties->getCopyOf(property_source->getKey()));
		 }
		else {
			propHolder->transactionBeginWrite();
			if ( propHolder->data == NULL ) {
				propHolder->transactionEnd();
				return request->replyResponse(WRET_ERR_INTERNAL, "Internal error (misaligned)");
			 }
			// eval permission, overwrite value (we won't update permission)
			bool allowWrite = SecurityManager::evalAuthorization( requestMessage->getSecurityToken(), &propHolder->data->getPermission(), Permission::Write );
			if ( !allowWrite ) {
				propHolder->transactionEnd();
				return request->replyResponse(WRET_ERR_SECURITY, "Not allowed to edit property");
			 }
			// go on with the atomic operation, update value/permission fields, callbacks enabled
			WRESULT ret = this->properties->updating( propHolder, property_source->getValue(), &property_source->getPermission(), Permission::Overwrite_All, true );
			propHolder->transactionEnd();
			// expensive, but useful to spread the updated property,
			// note that the whole operation is NOT atomic, following returned item MAY be != *property_source
			return request->replyResponse(ret, this->properties->getCopyOf(property_source->getKey()));
		 }
	 }
	else if ( request->getData()->isKindOf<List>() ) {
		Variant dKey;
		Variant dValue;
		request->extractArguments(dKey, dValue);
		if ( !dKey.isStringNotEmpty() )
			return request->replyResponse(WRET_ERR_PARAM, "Wrong DataType: List too short (key; !value)");
		mLOGv(LOG_DEBUG, ":MethodPropertySet(%s) [List]", dKey.asString().c_str() );
		PropertyHolder* propHolder = this->properties->find( dKey.asString() );
		if ( propHolder == NULL ) {
			// validate insertion against list permission, user must be granted to write the list
			bool allowAdd = SecurityManager::evalAuthorization( requestMessage->getSecurityToken(), &this->properties->getListPermission(), Permission::Write );
			if ( !allowAdd )
				return request->replyResponse(WRET_ERR_SECURITY, "Property not found, Not allowed to add new");
			// user may add the property, 
			///@todo should validate property permission..
			// add/update (non-locking, callbacks)
			WRESULT ret = this->properties->set( dKey.asString(), dValue, Permission(), Permission::Overwrite_None);
			// expensive, but useful to spread the updated property,
			// note that the whole operation is NOT atomic, following returned item MAY be != *property_source
			return request->replyResponse(ret, this->properties->getCopyOf(dKey.asString()));
		 }
		else {
			propHolder->transactionBeginWrite();
			if ( propHolder->data == NULL ) {
				propHolder->transactionEnd();
				return request->replyResponse(WRET_ERR_INTERNAL, "Internal error (misaligned)");
			 }
			// eval permission, overwrite value (we won't update permission)
			bool allowWrite = SecurityManager::evalAuthorization( requestMessage->getSecurityToken(), &propHolder->data->getPermission(), Permission::Write );
			if ( !allowWrite ) {
				propHolder->transactionEnd();
				return request->replyResponse(WRET_ERR_SECURITY, "Not authorized to edit property");
			 }
			// go on with the atomic operation, update value/permission fields, callbacks enabled
			WRESULT ret = this->properties->updating( propHolder, dValue, NULL, Permission::Overwrite_None, true );
			propHolder->transactionEnd();
			// expensive, but useful to spread the updated property,
			// note that the whole operation is NOT atomic, following returned item MAY be != *property_source
			return request->replyResponse(ret, this->properties->getCopyOf(dKey.asString()));
		 }
	 }
	mLOGv(LOG_WARNING, ":MethodPropertySet(%s) [Invalid Type]", request->getDataClassName().c_str() );
	return request->replyResponse(WRET_ERR_PARAM, "Wrong DataType: not Property/List!");
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* MethodPropertiesSet::execRequest( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	if ( request == NULL ) return NULL;
	if ( !request->hasData() ) return request->replyResponse(WRET_ERR_PARAM, "Empty Data");
	if ( this->properties == NULL ) {
		mLOG(LOG_WARNING, ":MethodPropertiesSet() PropertiesProvider reference is NULL" );
		return NULL;
	 }
	mLOG(LOG_DEBUG, ":MethodPropertiesSet(%s)" );
	// lock invalidating operations (REMOVAL; List::Permission updates)
	MutexLockerRead mL(this->properties->getMutex());
	if ( request->getData()->isKindOf<Table>() ) {
		const Table* table = request->getData()->as<Table>();
		if ( table->isEmpty() )
			return request->replyResponse(WRET_ERR_PARAM, "Invalid Argument (empty table)");
		return request->replyResponse(WRET_ERR_INTERNAL, "not implemented yet");
	 }
	return request->replyResponse(WRET_ERR_PARAM, "Invalid Argument (not a List/Table)");
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* MethodPropertyGet::execRequest( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	if ( request == NULL ) return NULL;
	if ( !request->hasData() ) return request->replyResponse(WRET_ERR_PARAM, "Empty Data");
	if ( this->properties == NULL ) {
		mLOG(LOG_WARNING, ":MethodPropertyGet() PropertiesProvider reference is NULL" );
		return NULL;
	 }
	bool allowread = SecurityManager::evalAuthorization( requestMessage->getSecurityToken(), &this->properties->getListPermission(), Permission::Read );
	if ( !allowread ) // also validated missing security token
		return request->replyResponse(WRET_ERR_SECURITY, "Not authorized to read");
	std::string key = request->getArgument().toString();
	mLOGv(LOG_DEBUG, ":MPC_mmDoPropertyGet(%s)", key.c_str() );
	MutexLockerRead mL(this->properties->getMutex());
	// lock invalidating operations (REMOVAL; List::Permission updates)
	const PropertyHolder* propHolder = this->properties->find( key );
	if ( propHolder == NULL )
		return request->replyResponse(WRET_ERR_PARAM, "Property not found");
	propHolder->transactionBeginRead();
	if ( propHolder->data == NULL ) {
		propHolder->transactionEnd();
		return request->replyResponse(WRET_ERR_INTERNAL, "Internal error (misaligned)");
	 }
	// eval permission, overwrite value (we won't update permission)
	bool allowRead = SecurityManager::evalAuthorization( requestMessage->getSecurityToken(), &propHolder->data->getPermission(), Permission::Read );
	if ( !allowRead ) {
		propHolder->transactionEnd();
		return request->replyResponse(WRET_ERR_SECURITY, "Not authorized to read property");
	 }
	// go on with the atomic operation, check if listener allows reading
	bool accepted = this->properties->reading( propHolder );
	if ( !accepted ) {
		propHolder->transactionEnd();
		return request->replyResponse(WRET_ERR_INTERNAL, "Internal error while reading");
	 }
	Property* prop = propHolder->data->clone();
	propHolder->transactionEnd();
	return request->replyResponse(WRET_OK, prop);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* MethodPropertiesGet::execRequest( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	if ( request == NULL ) return NULL;
	if ( !request->hasData() ) return request->replyResponse(WRET_ERR_PARAM, "Empty Data");
	if ( this->properties == NULL ) {
		mLOG(LOG_WARNING, ":MethodPropertiesGet() PropertiesProvider reference is NULL" );
		return NULL;
	 }
	bool allowread = SecurityManager::evalAuthorization( requestMessage->getSecurityToken(), &this->properties->getListPermission(), Permission::Read );
	if ( !allowread ) // also validated missing security token
		return request->replyResponse(WRET_ERR_SECURITY, "Not authorized to read");
	std::vector<std::string> keys;
	if ( request->getData()->isKindOf<Variant>() ) { // not optimized
		const Variant* vKeyRegex = request->getData()->as<Variant>();
		if ( vKeyRegex == NULL ) // should never happen
			return request->replyResponse(WRET_ERR_PARAM, "Invalid Variang Argument (internal failure!?)");
		std::string regex_key = vKeyRegex->toString();
		this->properties->transactionBeginRead();
		PropertiesProvider::ConstIterator it;
		const PropertiesProvider::ConstIterator it_end = this->properties->getMap().end();
		for( it=this->properties->getMap().begin(); it!=it_end; ++it ) {
			if ( it->second == NULL ) continue;
			if ( Utilities::evalRegexWildChar( regex_key, it->first ) == 0 )
				keys.push_back(it->first);
		 }
		this->properties->transactionEnd();
	 }
	else if ( request->getData()->isKindOf<List>() ) {
		const List* keyList = request->getData()->as<List>();
		if ( keyList == NULL ) // should never happen
			return request->replyResponse(WRET_ERR_PARAM, "Invalid List Argument (internal failure!?)");
		keyList->toVectorString(keys);
	 }
	mLOGv(LOG_DEBUG, ":MPC_mmDoPropertiesGet() %d items", keys.size() );
	if ( keys.empty() )
		return request->replyResponse(WRET_ERR_PARAM, "Invalid Argument (empty List)");
	List* outList = new List( keys.size() );
	int found = 0;
	this->properties->transactionBeginRead();
	std::vector<std::string>::const_iterator it;
	const std::vector<std::string>::const_iterator it_end = keys.end();
	for( it=keys.begin(); it!=it_end; ++it ) {
		const PropertyHolder* pH = this->properties->find( *it );
		if( pH == NULL ) continue;
		pH->transactionBeginRead();
		if( pH->data == NULL ) {
			pH->transactionEnd(); continue;
		 }
		++found;
		bool allowRead = SecurityManager::evalAuthorization( requestMessage->getSecurityToken(), &pH->data->getPermission(), Permission::Read );
		if ( !allowRead ) {
			pH->transactionEnd(); continue;
		 }
		allowRead = this->properties->reading(pH);
		if ( !allowRead ) {
			pH->transactionEnd(); continue;
		 }
		Property* copy = pH->data->clone();
		outList->append( copy );
		pH->transactionEnd();
	 }
	this->properties->transactionEnd();
	// for security reasons: don't send empty lists, it would provide confidential info on keys
	outList->cleanNULLs();
	if ( outList->isEmpty() ) {
		delete outList; outList = NULL;
	 }
	if ( found == 0 )
		return request->replyResponse(WRET_ERR_PARAM, "no keys found");
	return request->replyResponse(WRET_OK, outList);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

MethodChT::MethodChT( const std::string& Name )
	: Method(Name), properties(NULL), methods(NULL), object(NULL), log(NULL) { }

MethodChT::MethodChT( const std::string& Name, const std::string& Description, const Permission& Perm )
	: Method(Name, Description, Perm), properties(NULL), methods(NULL), object(NULL), log(NULL) { }

void MethodChT::setObject( Object* ptr ) { this->object = ptr; }
void MethodChT::setMethodsProvider( MethodsProvider* ptr ) { this->methods = ptr; }
void MethodChT::setPropertiesProvider( PropertiesProvider* ptr ) { this->properties = ptr; }
void MethodChT::setLogContext( LogContext* ptr ) { this->log = ptr; }

Message* MethodChT::exec( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	Response* response = execRequest(requestMessage);
	if ( response == NULL ) return NULL;
	return requestMessage->replyTo( response );
 }

WRESULT MethodChT::createMethods( MethodsProvider* provider, Object* obj, PropertiesProvider* props, LogContext* logcontext ) {
	if ( provider == NULL ) return WRET_ERR_PARAM;

	MethodChMod* mmChMod = provider->create<MethodChMod>();
	mmChMod->setObject( obj );
	mmChMod->setPropertiesProvider( props );
	mmChMod->setMethodsProvider( provider );
	mmChMod->setLogContext( logcontext );

	MethodChOwn* mmChOwn = provider->create<MethodChOwn>();
	mmChOwn->setObject( obj );
	mmChOwn->setPropertiesProvider( props );
	mmChOwn->setMethodsProvider( provider );
	mmChOwn->setLogContext( logcontext );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* MethodChMod_apply( Permission* targetPerm, const string& newMask, const Message* requestMessage ) {
	const Request* request = requestMessage->getContent()->asRequest();
	if ( targetPerm == NULL )
		return request->replyResponse(WRET_ERR_PARAM, "Invalid target");
	// recognize my dear god
	if ( requestMessage->getSecurityToken()->getUsername() != "root" &&
		 targetPerm->getOwner() != requestMessage->getSecurityToken()->getUsername() )
			return request->replyResponse(WRET_ERR_SECURITY, "User Not authorized");

	//if ( targetPerm->getMask(0) & Permission::Read == false )
	///@todo if ( perm.getMask(0) == Permission::Read ) SET enabled

	targetPerm->setMask( newMask );
	return request->replyResponse(WRET_OK, targetPerm->clone());
 }

Response* MethodChMod::execRequest( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() || !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	if ( !request->hasData() ) return request->replyResponse(WRET_ERR_PARAM, "Empty Data");
	// it should exist but let's ensure:
	if ( !requestMessage->hasSecurityToken() ) return NULL; //silent
	Response* response = NULL;
	// detect target
	// verify authorization
	// change permission
	if ( request->getData()->isKindOf<Variant>() ) {
		// so, for sure, it is referred to main object!
		if ( this->object == NULL )
			return request->replyResponse(WRET_ERR_INTERNAL, "Internal Error");
		///@warning not thread safe on 'object'
		Permission perm = ((const Object*)this->object)->getPermission();
		// recognize my dear god
		if ( requestMessage->getSecurityToken()->getUsername() != "root" &&
			perm.getOwner() != requestMessage->getSecurityToken()->getUsername() )
				return request->replyResponse(WRET_ERR_INTERNAL, "User Not authorized");
		if ( perm.getMask(0) == Permission::Read &&
			perm.getMask(1) == Permission::Read &&
			perm.getMask(2) == Permission::Read )
				return request->replyResponse(WRET_ERR_INTERNAL, "Rights Not authorized");
		string perms = request->getArgument().toString();
		mLOGv(LOG_INFO, ":MethodChMod(%s) %s", this->object->getName().c_str(), perms.c_str() );
		perm.setMask( perms );
		///@todo if ( perm.getMask(0) == Permission::Read ) SET enabled
		this->object->setPermission( perm );
		return request->replyResponse(WRET_OK, this->object->getPermission().clone());
	 }
	else if ( request->getData()->isKindOf<List>() ) {
		Variant dTarget;
		Variant dPerms;
		request->extractArguments(dTarget, dPerms);
		if ( !dTarget.isStringNotEmpty() || !dPerms.isStringNotEmpty() )
			return request->replyResponse(WRET_ERR_INTERNAL, "Invalid input [empty List]");
		// is it a property?
		if ( this->properties != NULL ) {
			this->properties->transactionBeginRead();
			PropertyHolder* prop = this->properties->find(dTarget.asString());
			if ( prop != NULL ) {
				prop->transactionBeginWrite();
				if ( prop->data == NULL ) {
					prop->transactionEnd();
					this->properties->transactionEnd();
					return request->replyResponse(WRET_ERR_INTERNAL, "Internal Error");
				 }
				response = MethodChMod_apply( &prop->data->getPermission(), dPerms.asString(), requestMessage );
				prop->transactionEnd();
				this->properties->transactionEnd();
				mLOGv(LOG_INFO, ":MethodChMod(%s) [Property] %s [by %s]", dTarget.asString().c_str(), dPerms.asString().c_str(), requestMessage->getSecurityToken()->getUsername().c_str() );
				return response;
			 }
			this->properties->transactionEnd();
		 }
		// so.. is it a method?
		if ( this->methods != NULL ) {
			this->methods->transactionBeginRead();
			Method* method = this->methods->find(dTarget.asString());
			if ( method != NULL ) {
				method->transactionBeginWrite();
				response = MethodChMod_apply( &method->getPermission(), dPerms.asString(), requestMessage );
				method->transactionEnd();
				this->methods->transactionEnd();
				mLOGv(LOG_INFO, ":MethodChMod(%s) [Method] %s [by %s]", dTarget.asString().c_str(), dPerms.asString().c_str(), requestMessage->getSecurityToken()->getUsername().c_str() );
				return response;
			 }
			this->methods->transactionEnd();
		 }
	 }
	else {
		mLOG(LOG_CRITICAL, ":MethodChMod(%s) not implemented" );
	 }
	return request->replyResponse(WRET_ERR_INTERNAL, "not implemented");
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* MethodChOwn_apply( Permission* targetPerm, const string& newOwner, const Message* requestMessage ) {
	const Request* request = requestMessage->getContent()->asRequest();
	if ( targetPerm == NULL )
		return request->replyResponse(WRET_ERR_PARAM, "Invalid target");
	// recognize my dear god
	if ( requestMessage->getSecurityToken()->getUsername() != "root" &&
		 targetPerm->getOwner() != requestMessage->getSecurityToken()->getUsername() )
			return request->replyResponse(WRET_ERR_SECURITY, "User Not authorized");

	if ( targetPerm->getMask(0) == Permission::Read && 
		 targetPerm->getMask(1) == Permission::Read && 
		 targetPerm->getMask(2) == Permission::Read )
			return request->replyResponse(WRET_ERR_SECURITY, "Rights Not authorized");

	targetPerm->setOwner( newOwner );
	return request->replyResponse(WRET_OK, targetPerm->clone());
 }


Response* MethodChOwn::execRequest( const Message* requestMessage ) {
	const Request* request = requestMessage->getContent()->asRequest();
	if ( !request->hasData() ) return request->replyResponse(WRET_ERR_PARAM, "Empty Data");
	// it should exist but let's ensure:
	if ( !requestMessage->hasSecurityToken() ) return NULL; //silent
	Response* response = NULL;
	// we must recognize the target { Object, Property, Method }
	if ( request->getData()->isKindOf<Variant>() ) {
		// single 'Data' argument surely refer to the object itself
		if ( this->object == NULL )
			return request->replyResponse(WRET_ERR_INTERNAL, "Internal Error");
		string newOwner = request->getArgument().toString();
		if ( !UserManager::isUser(newOwner) )
			return request->replyResponse(WRET_ERR_PARAM, "unknown User");
		///@warning not thread safe on 'object'
		Permission temp = ((const Object*)this->object)->getPermission();
		response = MethodChOwn_apply( &temp, newOwner, requestMessage );
		this->object->setPermission( temp );
		mLOGv(LOG_INFO, ":MethodChOwn(%s) %s [by %s]", this->object->getName().c_str(), newOwner.c_str(), requestMessage->getSecurityToken()->getUsername().c_str() );
		return response;
	 }
	else if ( request->getData()->isKindOf<List>() ) {
		Variant dTarget;
		Variant dNewOwner;
		request->extractArguments(dTarget, dNewOwner);
		if ( !dTarget.isStringNotEmpty() || !dNewOwner.isStringNotEmpty() )
			return request->replyResponse(WRET_ERR_PARAM, "Invalid input [empty List]");
		if ( !UserManager::isUser(dNewOwner.asString()) )
			return request->replyResponse(WRET_ERR_PARAM, "unknown User");
		// is it a property?
		if ( this->properties != NULL ) {
			this->properties->transactionBeginRead();
			PropertyHolder* prop = this->properties->find(dTarget.asString());
			if ( prop != NULL ) {
				prop->transactionBeginWrite();
				if ( prop->data == NULL ) {
					prop->transactionEnd();
					this->properties->transactionEnd();
					return request->replyResponse(WRET_ERR_INTERNAL, "Internal Error");
				 }
				response = MethodChOwn_apply( &prop->data->getPermission(), dNewOwner.asString(), requestMessage );
				prop->transactionEnd();
				this->properties->transactionEnd();
				mLOGv(LOG_INFO, ":MethodChOwn(%s) [Property] %s [by %s]", dTarget.asString().c_str(), dNewOwner.asString().c_str(), requestMessage->getSecurityToken()->getUsername().c_str() );
				return response;
			 }
			this->properties->transactionEnd();
		 }
		// so.. is it a method?
		if ( this->methods != NULL ) {
			this->methods->transactionBeginRead();
			Method* method = this->methods->find(dTarget.asString());
			if ( method != NULL ) {
				method->transactionBeginWrite();
				response = MethodChOwn_apply( &method->getPermission(), dNewOwner.asString(), requestMessage );
				method->transactionEnd();
				this->methods->transactionEnd();
				mLOGv(LOG_INFO, ":MethodChOwn(%s) [Method] %s [by %s]", dTarget.asString().c_str(), dNewOwner.asString().c_str(), requestMessage->getSecurityToken()->getUsername().c_str() );
				return response;
			 }
			this->methods->transactionEnd();
		 }
	 }
	return request->replyResponse(WRET_ERR_PARAM, "Target not Found");
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void MethodDump::setLogContext( LogContext* ptr ) { this->log = ptr; }

Message* MethodDump::exec( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	Response* response = execRequest(requestMessage);
	if ( response == NULL ) return NULL;
	return requestMessage->replyTo( response );
 }

Response* MethodDump::execRequest( const Message* requestMessage ) {
	const Request* request = requestMessage->getContent()->asRequest();
	if ( !request->hasData() ) return request->replyResponse(WRET_ERR_PARAM, "Empty Data");
	// it should exist but let's ensure:
	if ( !requestMessage->hasSecurityToken() ) return NULL; //silent

///@todo use PersistenceDb !?

	return request->replyResponse(WRET_ERR_PARAM, "Target not Found");
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
